/**
 * Copyright (C) 2011 Andrew C. Love (DNC) <dnc.app.sup@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.dnc.cloak.framework.util.dataconvert;

import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class DataConverter {

    static private final String NULL = "null";
    static public final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    static public final String DEFAULT_TIMESTAMP_FORMAT = "yyyy-MM-dd-HH.mm.ss.SSSSSS";

    static public Object getValue(Class<?> dataClass, Object data) throws DataConversionException {

        if (dataClass == String.class) {
            return getString(data);
        }
        else if (dataClass == Integer.class) {
            return getInteger(data);
        }
        else if (dataClass == Boolean.class) {
            return getBoolean(data);
        }
        else if (dataClass == Long.class) {
            return getLong(data);
        }
        else if (dataClass == Short.class) {
            return getShort(data);
        }
        else if (dataClass == Double.class) {
            return getDouble(data);
        }
        else if (dataClass == Date.class) {
            if (data instanceof String){
                return getDate((String)data,DEFAULT_DATE_FORMAT);
            }
            else
                return getDate(data);
        }
        else if (dataClass == Float.class) {
            return getFloat(data);
        }
        else if (dataClass == Number.class) {
            return getInteger(data);
        }
        else if (dataClass == Byte.class) {
            return getByte(data);
        }
        else if (dataClass == Character.class) {
            return getCharacter(data);
        }
        else {
            throw new DataConversionException("Data type not supported");
        }

    }

    static public Short getShort(Object o) throws DataConversionException {

        try {
            if (o == null  || o instanceof Short) {
                return (Short)o;
            }
            else if (o instanceof Number) {
                return new Short(((Number)o).shortValue());
            }
            else if (o instanceof Boolean) {
                return (((Boolean)o).booleanValue() ? new Short((short)1) : new Short((short)0));
            }
            else if (o instanceof String) {
                return ((isNull((String)o ) || o.toString().length() == 0) ? null : new Short(((String)o).trim()));
            }
            else if (o instanceof Character) {
                return new Short(((Character)o).toString());
            }
            else {
                throw new DataConversionException("Data type not supported");
            }
        }
        catch (Exception ex) {
            throw new DataConversionException(ex);
        }

    }

    static public Integer getInteger(Object o) throws DataConversionException {

        try {
            if (o == null || o instanceof Integer) {
                return (Integer)o;
            }
            else if (o instanceof Number) {
                return new Integer(((Number)o).intValue());
            }
            else if (o instanceof Boolean) {
                return (((Boolean)o).booleanValue() ? new Integer(1) : new Integer(0));
            }
            else if (o instanceof String) {
                return ((isNull((String)o) || o.toString().length() == 0) ? null : new Integer(((String)o).trim()));
            }
            else if (o instanceof Character) {
                return new Integer(((Character)o).toString());
            }
            else {
                throw new DataConversionException("Data type not supported");
            }
        }
        catch (Exception ex) {
            throw new DataConversionException(ex);
        }

    }

    static public Long getLong(Object o) throws DataConversionException {

        try {
            if (o == null || o instanceof Long) {
                return (Long)o;
            }
            else if (o instanceof Number) {
                return new Long(((Number)o).longValue());
            }
            else if (o instanceof Boolean) {
                return (((Boolean)o).booleanValue() ? new Long(1L) : new Long(0L));
            }
            else if (o instanceof String) {
                return ((isNull((String)o) || o.toString().length() == 0 )? null : new Long(((String)o).trim()));
            }
            else if (o instanceof Character) {
                return new Long(((Character)o).toString());
            }
            else {
                throw new DataConversionException("Data type not supported");
            }
        }
        catch (Exception ex) {
            throw new DataConversionException(ex);
        }

    }

    static public Float getFloat(Object o) throws DataConversionException {

        try {
            if (o == null || o instanceof Float) {
                return (Float)o;
            }
            else if (o instanceof Number) {
                return new Float(((Number)o).floatValue());
            }
            else if (o instanceof Boolean) {
                return (((Boolean)o).booleanValue() ? new Float((float)1.0) : new Float((float)0.0));
            }
            else if (o instanceof String) {
                return ((isNull((String)o) || o.toString().length() == 0 )? null : new Float(((String)o).trim()));
            }
            else if (o instanceof Character) {
                return new Float(((Character)o).toString());
            }
            else {
                throw new DataConversionException("Data type not supported");
            }

        }
        catch (Exception ex) {
            throw new DataConversionException(ex);
        }

    }

    static public Double getDouble(Object o) throws DataConversionException {

        try {
            if (o == null || o instanceof Double) {
                return (Double)o;
            }
            else if (o instanceof Number) {
                return new Double(((Number)o).doubleValue());
            }
            else if (o instanceof Boolean) {
                return (((Boolean)o).booleanValue() ? new Double(1.0) : new Double(0.0));
            }
            else if (o instanceof String) {
                return ((isNull((String)o) || o.toString().length() == 0) ? null : new Double(((String)o).trim()));
            }
            else if (o instanceof Character) {
                return new Double(((Character)o).toString());
            }
            else {
                throw new DataConversionException("Data type not supported");
            }
        }
        catch (Exception ex) {
            throw new DataConversionException(ex);
        }

    }

    static public Byte getByte(Object o) throws DataConversionException {

        try {
            if (o == null || o instanceof Byte) {
                return (Byte)o;
            }
            else if (o instanceof Number) {
                return new Byte(((Number)o).byteValue());
            }
            else if (o instanceof Boolean) {
                return (((Boolean)o).booleanValue() ? new Byte((byte)1) : new Byte((byte)0));
            }
            else if (o instanceof String) {
                return ((isNull((String)o) || o.toString().length() == 0 )? null : new Byte(((String)o).trim()));
            }
            else if (o instanceof Character) {
                return new Byte(((Character)o).toString());
            }
            else {
                throw new DataConversionException("Data type not supported");
            }
        }
        catch (Exception ex) {
            throw new DataConversionException(ex);
        }

    }

    static public Boolean getBoolean(Object o) throws DataConversionException {

        try {
            if (o == null || o instanceof Boolean) {
                return (Boolean)o;
            }
            else if (o instanceof Number) {
                try {
                    return new Boolean(((Number)o).intValue() != 0);
                }
                catch (Exception ex) {   // too big to cast back to an int?
                    return Boolean.FALSE;
                }
            }
            else if (o instanceof String) {
                return ((isNull((String)o) || o.toString().length() == 0)? Boolean.FALSE : new Boolean(((String)o).trim().equalsIgnoreCase("true")));
            }
            else if (o instanceof Character) {
                return new Boolean(Character.toLowerCase(((Character)o).charValue()) == 't' );
            }
            else {
                throw new DataConversionException("Data type not supported");
            }
        }
        catch (Exception ex) {
            throw new DataConversionException(ex);
        }

    }

    static public String getString(Date date, String mask) throws DataConversionException {

        if (date == null) {
            return null;
        }

        try {
            SimpleDateFormat format = new SimpleDateFormat(mask);
            return format.format(date);
        }
        catch (Exception ex) {
            throw new DataConversionException(ex);
        }

    }

    static public String getString(Object o, String mask) throws DataConversionException {

        if (o == null) {
            return (String)o;
        }
        else if (o instanceof Date) {
            return getString((Date)o, mask);
        }
        else {
            throw new DataConversionException("Data type not supported");
        }
    }

    static public String getString(Object o) throws DataConversionException {

        try {
            if (o == null) {
                return (String)o;
            }
            else if (o instanceof String) {
                return (isNull((String)o) ? null : (String)o);
            }
            else {
                return o.toString();
            }
        }
        catch (Exception ex) {
            throw new DataConversionException(ex);
        }

    }

    static public Character getCharacter(Object o) throws DataConversionException {

        try {
            if (o == null || o instanceof Character) {
                return (Character)o;
            }
            else {
                String str = o.toString();
                if (str.length() > 0)
                    return new Character(str.charAt(0));
                else
                    return null;
            }
        }
        catch (Exception ex) {
            throw new DataConversionException(ex);
        }

    }

    static public String getDayOfWeek(int day) throws DataConversionException {

        String dayText = "";

        switch (day) {
        case 1:
            dayText = "Sunday";
            break;
        case 2:
            dayText = "Monday";
            break;
        case 3:
            dayText = "Tuesday";
            break;
        case 4:
            dayText = "Wednesday";
            break;
        case 5:
            dayText = "Thursday";
            break;
        case 6:
            dayText = "Friday";
            break;
        case 7:
            dayText = "Saturday";
            break;
        }

        return dayText;

    }

    static public String dow(Integer day) throws DataConversionException {

        String dayText = "";

        switch (day) {
        case 1:
            dayText = "Sunday";
            break;
        case 2:
            dayText = "Monday";
            break;
        case 3:
            dayText = "Tuesday";
            break;
        case 4:
            dayText = "Wednesday";
            break;
        case 5:
            dayText = "Thursday";
            break;
        case 6:
            dayText = "Friday";
            break;
        case 7:
            dayText = "Saturday";
            break;
        }

        return dayText;

    }


    static public Date getDate(String date, String mask) throws DataConversionException {

        try {
            if (date == null) {
                return null;
            }

            SimpleDateFormat format = new SimpleDateFormat(mask);

            return format.parse(date);
        }
        catch (Exception ex) {
            throw new DataConversionException(ex);
        }

    }

    static public Date getDate(Object o) throws DataConversionException {

        try {
            if (o == null  || o instanceof Date) {
                return (Date)o;
            }
            else if (o instanceof Number) {
                return new Date(((Number)o).longValue());
            }
            else if (o instanceof String) {
                return (isNull((String)o) ? null : new Date(Long.parseLong(((String)o).trim())));
            }
            else if (o instanceof Character) {
                return new Date(Long.parseLong(((Character)o).toString().trim()));
            }
            else {
                throw new DataConversionException("Data type not supported");
            }
        }
        catch (Exception ex) {
            throw new DataConversionException(ex);
        }

    }

    static public java.sql.Date getSQLDate(Object o) throws DataConversionException {

        try {
            if (o == null  || o instanceof java.sql.Date) {
                return (java.sql.Date)o;
            }
            else if (o instanceof java.sql.Timestamp) {
                return new java.sql.Date(((java.sql.Timestamp)o).getTime());
            }
            else if (o instanceof Number) {
                return new java.sql.Date(((Number)o).longValue());
            }
            else if (o instanceof String) {
                if (isNull((String)o) || ((String)o).trim().length() == 0) {
                    return null;
                }
                else {
                    if (((String)o).indexOf('-') == -1) {
                        return new java.sql.Date(Long.parseLong(((String)o).trim()));
                    }
                    else {
                        return java.sql.Date.valueOf(((String)o).trim());
                    }
                }
            }
            else if (o instanceof Character) {
                return new java.sql.Date(Long.parseLong(((Character)o).toString().trim()));
            }
            else {
                throw new DataConversionException("Data type not supported");
            }
        }
        catch (Exception ex) {
            throw new DataConversionException(ex);
        }

    }

    static public java.sql.Time getSqlTime(Object o) throws DataConversionException {

        try {
            if (o == null  || o instanceof java.sql.Time) {
                return (java.sql.Time)o;
            }
            else if (o instanceof Number) {
                return new java.sql.Time(((Number)o).longValue());
            }
            else if (o instanceof String) {
                if (isNull((String)o) || ((String)o).trim().length() == 0) {
                    return null;
                }
                else {
                    if (((String)o).indexOf('-') == -1 && ((String)o).indexOf(':') == -1) {
                        return new java.sql.Time(Long.parseLong(((String)o).trim()));
                    }
                    else {
                        return java.sql.Time.valueOf(((String)o).trim());
                    }
                }
            }
            else if (o instanceof Character) {
                return new java.sql.Time(Long.parseLong(((Character)o).toString().trim()));
            }
            else {
                throw new DataConversionException("Data type not supported");
            }
        }
        catch (Exception ex) {
            throw new DataConversionException(ex);
        }

    }

    static public java.sql.Timestamp getSqlTimestamp(Object o) throws DataConversionException {

        try {
            if (o == null  || o instanceof java.sql.Timestamp) {
                return (java.sql.Timestamp)o;
            }
            else if (o instanceof Number) {
                return new java.sql.Timestamp(((Number)o).longValue());
            }
            else if (o instanceof String) {
                if (isNull((String)o) || ((String)o).trim().length() == 0) {
                    return null;
                }
                else {
                    if (((String)o).indexOf('-') == -1 && ((String)o).indexOf(':') == -1) {
                        return new java.sql.Timestamp(Long.parseLong(((String)o).trim()));
                    }
                    else {
                        try {
                            return java.sql.Timestamp.valueOf(((String)o).trim());
                        }
                        catch (Exception ex) {
                            throw new DataConversionException("Data type not supported",ex);
                        }
                    }
                }
            }
            else if (o instanceof Character) {
                return new java.sql.Timestamp(Long.parseLong(((Character)o).toString().trim()));
            }
            else {
                throw new DataConversionException("Data type not supported");
            }
        }
        catch (Exception ex) {
            throw new DataConversionException(ex);
        }

    }

    static protected boolean isNull(String str) {
        return str.trim().equals(NULL);
    }

    static public Date getNextDay (int day, int minDays) {

        Calendar cal = Calendar.getInstance();
        int days = 0;
        while(true && days < 365){
            days++;
            cal.add(Calendar.DATE, 1);
            int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);

            if (dayOfWeek == day && days > minDays) {
                break;
            }
        }

        return cal.getTime();

    }

    static public BigDecimal getBigDecimal(String val) throws DataConversionException {

        BigDecimal returnObj;
        try {
            returnObj = new BigDecimal(val);
        }
        catch (Exception e) {
            throw new DataConversionException(e);
        }

        return returnObj;

    }

    static public long getCurrentDateAsLong() throws DataConversionException {

        long d;
        try {
            d = System.currentTimeMillis();
        }
        catch (Exception e) {
            throw new DataConversionException(e);
        }

        return d;

    }

    public static URL getURL(String spec) throws DataConversionException {

        URL returnObj = null;
        try {
            returnObj = new URL(spec);
        }
        catch (MalformedURLException e) {
            throw new DataConversionException(e);
        }

        return returnObj;

    }

    static public Calendar clearTime(Calendar cal) {

        cal.set(Calendar.AM_PM, Calendar.AM);
        cal.set(Calendar.HOUR, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal;

    }

}